package com.baomidou.springwind.pojo;

import com.baomidou.springwind.entity.NetworkDelivery;
import com.baomidou.springwind.utils.CityUtil;

import java.math.BigDecimal;
import java.util.*;

/**
 * 一级配送中心覆盖二级配送中心
 * 采用伏格尔法：在差额最大处，采用最小运费调运；
 *
 * @Author demo
 * @Date 2017-8-6
 */
public class SiteCoverBuilder {

    /**
     * 一级配送中心
     */
    private List<SiteSelection> firstSites;

    /**
     * 二级配送中心
     */
    private List<NetworkDelivery> secondSites;


    public SiteCoverBuilder(List<SiteSelection> firstSites, List<NetworkDelivery> secondSites) {
        this.firstSites = firstSites;
        this.secondSites = secondSites;
    }

    public Map<SiteSelection, List<SiteSelection>> build() {
        Map<SiteSelection, List<SiteSelection>> result = new HashMap<SiteSelection, List<SiteSelection>>();
        Double[][] table = new Double[firstSites.size()][secondSites.size()];
        List<Long> cList = new ArrayList<Long>(firstSites.size());
        List<Long> vList = new ArrayList<Long>(secondSites.size());
        for (int i = 0; i < firstSites.size(); i++) {
            SiteSelection ss = firstSites.get(i);
            for (int j = 0; j < secondSites.size(); j++) {
                NetworkDelivery nd = secondSites.get(j);
                table[i][j] = CityUtil.getDistance(ss.getCoordinateX().doubleValue(), ss.getCoordinateY().doubleValue(),
                        nd.getCoordinateX().doubleValue(), nd.getCoordinateY().doubleValue());
            }
            cList.add(ss.getOrder().longValue());
        }
        for (NetworkDelivery delivery : secondSites) {
            vList.add(delivery.getOrderQuantity().longValue());
        }
        while (isAllAssign(table)) {
            Pos pos = getMaxPN(table, cList, vList);
            Tuc tuc = getMinTuc(table, pos);
            Long cValue = cList.get(tuc.row);
            Long vValue = vList.get(tuc.cell);
            if (cValue.compareTo(vValue) == 0) {
                vList.set(tuc.cell, 0L);
                cList.set(tuc.row, 0L);
                for (int i = 0; i < table.length; i++) {
                    table[i][tuc.cell] = -1D;
                }
                for (int i = 0; i < table[0].length; i++) {
                    table[tuc.row][i] = -1D;
                }
            } else if (cValue.compareTo(vValue) > 0) {
                vList.set(tuc.cell, 0L);
                cList.set(tuc.row, cValue - vValue);
                for (int i = 0; i < table.length; i++) {
                    table[i][tuc.cell] = -1D;
                }
            } else {
                cList.set(tuc.row, 0L);
                vList.set(tuc.cell, vValue - cValue);
                for (int i = 0; i < table[0].length; i++) {
                    table[tuc.row][i] = -1D;
                }
            }
            SiteSelection first = firstSites.get(tuc.row);
            SiteSelection second;
            List<SiteSelection> coverList;
            if (result.containsKey(first)) {
                coverList = result.get(first);
            } else {
                coverList = new ArrayList<SiteSelection>();
                result.put(first, coverList);
            }
            second = SiteSelection.transform(secondSites.get(tuc.cell));
            second.setOrder(new BigDecimal(Math.min(cValue, vValue)));
            coverList.add(second);
        }
        return result;
    }

    /**
     * 二级中心是否全部分配
     *
     * @param table
     * @return
     */
    private boolean isAllAssign(Double[][] table) {
        for (int i = 0; i < table.length; i++) {
            for (int j = 0; j < table[i].length; j++) {
                if (table[i][j] >= 0) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 获取最小单位运价的行列
     *
     * @param table
     * @param pos
     */
    private Tuc getMinTuc(Double[][] table, Pos pos) {
        int row = 0;
        int cell = 0;
        Double minTuc = Double.MAX_VALUE;
        if (pos.cell == null) {
            for (int i = 0; i < table[0].length; i++) {
                if (table[pos.row][i] != -1 && table[pos.row][i] < minTuc) {
                    minTuc = table[pos.row][i];
                    row = pos.row;
                    cell = i;
                }
            }
        } else {
            for (int i = 0; i < table.length; i++) {
                if (table[i][pos.cell] != -1 && table[i][pos.cell] < minTuc) {
                    minTuc = table[i][pos.cell];
                    row = i;
                    cell = pos.cell;
                }
            }
        }
        return new Tuc(row, cell);
    }

    /**
     * 获取最大罚款数
     *
     * @param table 单位运价表
     * @return 最大罚款数pn
     */
    private Pos getMaxPN(Double[][] table, List<Long> cList, List<Long> vList) {
        List<Pos> posList = new ArrayList<Pos>();
        int row = table.length;
        int cell = table[0].length;
        for (int i = 0; i < row; i++) {
            List<Double> rowList = new ArrayList<Double>(cell);
            for (int j = 0; j < cell; j++) {
                if (table[i][j] >= 0) {
                    rowList.add(table[i][j]);
                }
            }
            if (rowList.size() == 0 || cList.get(i) == 0) {
                continue;
            } else if (rowList.size() == 1) {
                posList.add(new Pos(i, null, rowList.get(0)));
            } else {
                Collections.sort(rowList);
                Double pn = rowList.get(1) - rowList.get(0);
                posList.add(new Pos(i, null, pn));
            }
        }
        for (int i = 0; i < cell; i++) {
            List<Double> cellList = new ArrayList<Double>(row);
            for (int j = 0; j < row; j++) {
                if (table[j][i] >= 0) {
                    cellList.add(table[j][i]);
                }
            }
            if (cellList.size() == 0 || vList.get(i) == 0) {
                continue;
            } else if (cellList.size() == 1) {
                posList.add(new Pos(null, i, cellList.get(0)));
            } else {
                Collections.sort(cellList);
                Double pn = cellList.get(1) - cellList.get(0);
                posList.add(new Pos(null, i, pn));
            }
        }
        Collections.sort(posList, new Comparator<Pos>() {
            @Override
            public int compare(Pos o1, Pos o2) {
                return o2.pn.compareTo(o1.pn);
            }
        });
        return posList.get(0);
    }

    private static class Pos {
        final Integer row;
        final Integer cell;
        final Double pn;

        public Pos(Integer row, Integer cell, Double pn) {
            this.row = row;
            this.cell = cell;
            this.pn = pn;
        }
    }

    private static class Tuc {
        final Integer row;
        final Integer cell;

        public Tuc(Integer row, Integer cell) {
            this.row = row;
            this.cell = cell;
        }
    }
}
